package com.hl.tools.utils;

import org.apache.commons.lang3.StringUtils;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.params.SetParams;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by 31464 on 2021/10/20.
 *
 * 利用Redis做分布式锁
 *   注意：
 *     1 一定要加过期时间，否则如果程序挂了，没有删除锁的话，会造成死锁
 *     2 程序运行时，要注意更新过期时间，比如 一个程序执行时间需要60秒，而过期时间设定为30秒，程序还没有执行完成，锁就自动释放了
 *
 */
public class RedisLock {

    private RedisLock(){}

    private static final String LOCK_SUCCESS="OK";
    private static final Long RELEASE_SUCCESS=1L;


    /**
     * 获取分布式锁
     *   如果锁存在，证明所已经获取，返回false,否则返回true
     * @param jedis
     * @param lockKey        锁
     * @param requestId
     * @param expirTime       超时时间 秒
     * @return   true 获取成功， false 获取失败
     */
    public static boolean tryGetDistributeLock(JedisCluster jedis,String lockKey,String requestId,int expirTime){
        SetParams setParams=new SetParams();
        setParams.ex(expirTime);
        setParams.nx();
        setParams.px(expirTime*1000);
        String result = jedis.set(lockKey,requestId,setParams);
        return StringUtils.equalsIgnoreCase(LOCK_SUCCESS,result)?true:false;
    }


    /**
     * 如果拥有这个锁，则更新锁的时间
     *    注意：更新锁的频率要小于过期时间
     * @param jedis
     * @param lockKey
     * @param requestId
     * @param expireTime
     * @return
     */
    public static boolean updateOwnLockTime(JedisCluster jedis,String lockKey,String requestId,int expireTime){
        List<String> argv=new ArrayList<>();
        argv.add(requestId);
        argv.add(""+ expireTime);
        String script="if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), argv);
        if(RELEASE_SUCCESS.equals(result)){
            return true;
        }
        return false;
    }


    /**
     * 释放锁，实际上就是删除key(判断value是否一直才删除，防止误删)
     * @param jedis
     * @param lockKey
     * @param requestId
     * @return
     */
    public static boolean releaseDistributedLock(JedisCluster jedis,String lockKey,String requestId){
        String script="if redis.call('get',KEYS[1] == ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        return RELEASE_SUCCESS.equals(result)? true:false;
    }


}
